﻿// SciTE - Scintilla based Text Editor
/** @file SciTEProps.cxx
 ** Properties management.
 **/
// Copyright 1998-2004 by Neil Hodgson <neilh@scintilla.org>
// The License.txt file describes the conditions under which this software may be distributed.

#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <stdio.h>
#include <fcntl.h>
#include <time.h>
#include <locale.h>

#include <string>
#include <map>

#include "Platform.h"

#if PLAT_WIN

#include <windows.h>
#include <commctrl.h>

const WCHAR menuAccessIndicator[] = L"&";

#endif

#include "SciTE.h"
#include "PropSet.h"
#include "SString.h"
#include "StringList.h"
#include "Accessor.h"
#include "Scintilla.h"
#include "SciLexer.h"
#include "Extender.h"
#include "FilePath.h"
#include "PropSetFile.h"
#include "Mutex.h"
#include "JobQueue.h"
#include "SciTEBase.h"
#include "IFaceTable.h"

void SciTEBase::SetImportMenu() {
	for (int i = 0; i < importMax; i++) {
		DestroyMenuItem(menuOptions, importCmdID + i);
	}
	if (importFiles[0].IsSet()) {
		for (int stackPos = 0; stackPos < importMax; stackPos++) {
			int itemID = importCmdID + stackPos;
			if (importFiles[stackPos].IsSet()) {
				SString entry = localiser.Text(L"Open");
				entry += L" ";
				entry += importFiles[stackPos].Name().AsInternal();
				SetMenuItem(menuOptions, IMPORT_START + stackPos, itemID, entry.c_str());
			}
		}
	}
}

void SciTEBase::ImportMenu(int pos) {
	//Platform::DebugPrintf("Stack menu %d\n", pos);
	if (pos >= 0) {
		if (importFiles[pos].IsSet()) {
			Open(importFiles[pos]);
		}
	}
}

void SciTEBase::SetLanguageMenu() {
	for (int i = 0; i < 100; i++) {
		DestroyMenuItem(menuLanguage, languageCmdID + i);
	}
	for (int item = 0; item < languageItems; item++) {
		int itemID = languageCmdID + item;
		SString entry = localiser.Text(languageMenu[item].menuItem.c_str());
		if (languageMenu[item].menuKey.length()) {
#if PLAT_GTK
			entry += " ";
#else
			entry += "\t";
#endif
			entry += languageMenu[item].menuKey;
		}
		if (entry[0] != '#') {
			SetMenuItem(menuLanguage, item, itemID, entry.c_str());
		}
	}
}

const WCHAR propLocalFileName[] = L"SciTE.properties";
const WCHAR propDirectoryFileName[] = L"SciTEDirectory.properties";

/**
Read global and user properties files.
*/
void SciTEBase::ReadGlobalPropFile() {
	WCHAR **e=_wenviron;
	for (; *e; e++) {
		WCHAR key[1024];
		WCHAR *k=*e;
		WCHAR *v=wcschr(k,L'=');
		if (v && (static_cast<size_t>(v-k) < sizeof(key) / sizeof(WCHAR))) {
			memcpy(key, k, (v-k) * sizeof(WCHAR));
			key[v-k] = L'\0';
			propsEmbed.Set(key, v+1);
		}
	}

	for (int stackPos = 0; stackPos < importMax; stackPos++) {
		importFiles[stackPos] = L"";
	}

	propsBase.Clear();
	FilePath propfileBase = GetDefaultPropertiesFileName();
	propsBase.Read(propfileBase, propfileBase.Directory(), importFiles, importMax);

	propsUser.Clear();
	FilePath propfileUser = GetUserPropertiesFileName();
	propsUser.Read(propfileUser, propfileUser.Directory(), importFiles, importMax);

	if (!localiser.read) {
		ReadLocalization();
	}
}

void SciTEBase::ReadAbbrevPropFile() {
	propsAbbrev.Clear();
	propsAbbrev.Read(pathAbbreviations, pathAbbreviations.Directory(), importFiles, importMax);
}

/**
Reads the directory properties file depending on the variable
"properties.directory.enable". Also sets the variable $(SciteDirectoryHome) to the path
where this property file is found. If it is not found $(SciteDirectoryHome) will
be set to $(FilePath).
*/
void SciTEBase::ReadDirectoryPropFile() {
	propsDirectory.Clear();

	if (props.GetInt(L"properties.directory.enable") != 0) {
		FilePath propfile = GetDirectoryPropertiesFileName();
		props.Set(L"SciteDirectoryHome", propfile.Directory().AsFileSystem());

		propsDirectory.Read(propfile, propfile.Directory());
	}
}

/**
Read local and directory properties file.
*/
void SciTEBase::ReadLocalPropFile() {
	// The directory properties acts like a base local properties file.
	// Therefore it must be read always before reading the local prop file.
	ReadDirectoryPropFile();

	FilePath propfile = GetLocalPropertiesFileName();

	propsLocal.Clear();
	propsLocal.Read(propfile, propfile.Directory());
	//Platform::DebugPrintf("Reading local properties from %s\n", propfile);

	// TODO: Grab these from Platform and update when environment says to
	props.Set(L"Chrome", L"#C0C0C0");
	props.Set(L"ChromeHighlight", L"#FFFFFF");
}

int IntFromHexDigit(int ch) {
	if ((ch >= '0') && (ch <= '9')) {
		return ch - '0';
	} else if (ch >= 'A' && ch <= 'F') {
		return ch - 'A' + 10;
	} else if (ch >= 'a' && ch <= 'f') {
		return ch - 'a' + 10;
	} else {
		return 0;
	}
}

int IntFromHexByte(const WCHAR *hexByte) {
	return IntFromHexDigit(hexByte[0]) * 16 + IntFromHexDigit(hexByte[1]);
}

static long ColourFromString(const SString &s) {
	if (s.length()) {
		int r = IntFromHexByte(s.c_str() + 1);
		int g = IntFromHexByte(s.c_str() + 3);
		int b = IntFromHexByte(s.c_str() + 5);
		return ColourDesired(r, g, b).AsLong();
	} else {
		return ColourDesired().AsLong();
	}
}

long ColourOfProperty(PropSetFile &props, const WCHAR *key, ColourDesired colourDefault) {
	SString colour = props.GetExpanded(key);
	if (colour.length()) {
		return ColourFromString(colour);
	}
	return colourDefault.AsLong();
}

/**
 * Put the next property item from the given property string
 * into the buffer pointed by @a pPropItem.
 * @return NULL if the end of the list is met, else, it points to the next item.
 */
const WCHAR *SciTEBase::GetNextPropItem(
	const WCHAR *pStart,	/**< the property string to parse for the first call,
						 * pointer returned by the previous call for the following. */
	WCHAR *pPropItem,	///< pointer on a buffer receiving the requested prop item
	int maxLen)			///< size of the above buffer
{
	int size = maxLen - 1;

	*pPropItem = '\0';
	if (pStart == NULL) {
		return NULL;
	}
	const WCHAR *pNext = wcschr(pStart, ',');
	if (pNext) {	// Separator is found
		if (size > pNext - pStart) {
			// Found string fits in buffer
			size = pNext - pStart;
		}
		pNext++;
	}
	wcsncpy(pPropItem, pStart, size);
	pPropItem[size] = L'\0';
	return pNext;
}

StyleDefinition::StyleDefinition(const WCHAR *definition) :
		size(0), fore(L"#000000"), back(L"#FFFFFF"),
		bold(false), italics(false), eolfilled(false), underlined(false),
		caseForce(SC_CASE_MIXED),
		visible(true), changeable(true),
		specified(sdNone) {
	ParseStyleDefinition(definition);
}

bool StyleDefinition::ParseStyleDefinition(const WCHAR *definition) {
	if (definition == 0 || *definition == L'\0') {
		return false;
	}
	WCHAR *val = StringDup(definition);
	//Platform::DebugPrintf("Style %d is [%s]\n", style, val);
	WCHAR *opt = val;
	while (opt) {
		// Find attribute separator
		WCHAR *cpComma = wcschr(opt, L',');
		if (cpComma) {
			// If found, we terminate the current attribute (opt) string
			*cpComma = L'\0';
		}
		// Find attribute name/value separator
		WCHAR *colon = wcschr(opt, L':');
		if (colon) {
			// If found, we terminate the current attribute name and point on the value
			*colon++ = L'\0';
		}
		if (0 == wcscmp(opt, L"italics")) {
			specified = static_cast<flags>(specified | sdItalics);
			italics = true;
		}
		if (0 == wcscmp(opt, L"notitalics")) {
			specified = static_cast<flags>(specified | sdItalics);
			italics = false;
		}
		if (0 == wcscmp(opt, L"bold")) {
			specified = static_cast<flags>(specified | sdBold);
			bold = true;
		}
		if (0 == wcscmp(opt, L"notbold")) {
			specified = static_cast<flags>(specified | sdBold);
			bold = false;
		}
		if (0 == wcscmp(opt, L"font")) {
			specified = static_cast<flags>(specified | sdFont);
			font = colon;
			font.substitute(L'|', L',');
		}
		if (0 == wcscmp(opt, L"fore")) {
			specified = static_cast<flags>(specified | sdFore);
			fore = colon;
		}
		if (0 == wcscmp(opt, L"back")) {
			specified = static_cast<flags>(specified | sdBack);
			back = colon;
		}
		if (0 == wcscmp(opt, L"size")) {
			specified = static_cast<flags>(specified | sdSize);
			size = _wtoi(colon);
		}
		if (0 == wcscmp(opt, L"eolfilled")) {
			specified = static_cast<flags>(specified | sdEOLFilled);
			eolfilled = true;
		}
		if (0 == wcscmp(opt, L"noteolfilled")) {
			specified = static_cast<flags>(specified | sdEOLFilled);
			eolfilled = false;
		}
		if (0 == wcscmp(opt, L"underlined")) {
			specified = static_cast<flags>(specified | sdUnderlined);
			underlined = true;
		}
		if (0 == wcscmp(opt, L"notunderlined")) {
			specified = static_cast<flags>(specified | sdUnderlined);
			underlined = false;
		}
		if (0 == wcscmp(opt, L"case")) {
			specified = static_cast<flags>(specified | sdCaseForce);
			caseForce = SC_CASE_MIXED;
			if (colon) {
				if (*colon == L'u')
					caseForce = SC_CASE_UPPER;
				else if (*colon == L'l')
					caseForce = SC_CASE_LOWER;
			}
		}
		if (0 == wcscmp(opt, L"visible")) {
			specified = static_cast<flags>(specified | sdVisible);
			visible = true;
		}
		if (0 == wcscmp(opt, L"notvisible")) {
			specified = static_cast<flags>(specified | sdVisible);
			visible = false;
		}
		if (0 == wcscmp(opt, L"changeable")) {
			specified = static_cast<flags>(specified | sdChangeable);
			changeable = true;
		}
		if (0 == wcscmp(opt, L"notchangeable")) {
			specified = static_cast<flags>(specified | sdChangeable);
			changeable = false;
		}
		if (cpComma)
			opt = cpComma + 1;
		else
			opt = 0;
	}
	delete []val;
	return true;
}

long StyleDefinition::ForeAsLong() const {
	return ColourFromString(fore);
}

long StyleDefinition::BackAsLong() const {
	return ColourFromString(back);
}

void SciTEBase::SetOneStyle(Window &win, int style, const StyleDefinition &sd) {
	if (sd.specified & StyleDefinition::sdItalics)
		Platform::SendScintilla(win.GetID(), SCI_STYLESETITALIC, style, sd.italics ? 1 : 0);
	if (sd.specified & StyleDefinition::sdBold)
		Platform::SendScintilla(win.GetID(), SCI_STYLESETBOLD, style, sd.bold ? 1 : 0);
	if (sd.specified & StyleDefinition::sdFont)
		Platform::SendScintillaPointer(win.GetID(), SCI_STYLESETFONT, style,
			const_cast<WCHAR *>(sd.font.c_str()));
	if (sd.specified & StyleDefinition::sdFore)
		Platform::SendScintilla(win.GetID(), SCI_STYLESETFORE, style, sd.ForeAsLong());
	if (sd.specified & StyleDefinition::sdBack)
		Platform::SendScintilla(win.GetID(), SCI_STYLESETBACK, style, sd.BackAsLong());
	if (sd.specified & StyleDefinition::sdSize)
		Platform::SendScintilla(win.GetID(), SCI_STYLESETSIZE, style, sd.size);
	if (sd.specified & StyleDefinition::sdEOLFilled)
		Platform::SendScintilla(win.GetID(), SCI_STYLESETEOLFILLED, style, sd.eolfilled ? 1 : 0);
	if (sd.specified & StyleDefinition::sdUnderlined)
		Platform::SendScintilla(win.GetID(), SCI_STYLESETUNDERLINE, style, sd.underlined ? 1 : 0);
	if (sd.specified & StyleDefinition::sdCaseForce)
		Platform::SendScintilla(win.GetID(), SCI_STYLESETCASE, style, sd.caseForce);
	if (sd.specified & StyleDefinition::sdVisible)
		Platform::SendScintilla(win.GetID(), SCI_STYLESETVISIBLE, style, sd.visible ? 1 : 0);
	if (sd.specified & StyleDefinition::sdChangeable)
		Platform::SendScintilla(win.GetID(), SCI_STYLESETCHANGEABLE, style, sd.changeable ? 1 : 0);
	Platform::SendScintilla(win.GetID(), SCI_STYLESETCHARACTERSET, style, characterSet);
}

void SciTEBase::SetStyleFor(Window &win, const WCHAR *lang) {
	for (int style = 0; style <= STYLE_MAX; style++) {
		if (style != STYLE_DEFAULT) {
			WCHAR key[200];
			wsprintf(key, L"style.%s.%0d", lang, style);
			SString sval = props.GetExpanded(key);
			if (sval.length()) {
				SetOneStyle(win, style, sval.c_str());
			}
		}
	}
}

void LowerCaseString(WCHAR *s) {
	while (*s) {
		if ((*s >= L'A') && (*s <= L'Z')) {
			*s = static_cast<WCHAR>(*s - L'A' + L'a');
		}
		s++;
	}
}

SString SciTEBase::ExtensionFileName() {
	if (CurrentBuffer()->overrideExtension.length()) {
		return CurrentBuffer()->overrideExtension;
	} else {
		FilePath name = FileNameExt();
		if (name.IsSet()) {
			// Force extension to lower case
			WCHAR fileNameWithLowerCaseExtension[MAX_PATH];
			wcscpy(fileNameWithLowerCaseExtension, name.AsInternal());
#if PLAT_WIN
			WCHAR *extension = wcsrchr(fileNameWithLowerCaseExtension, '.');
			if (extension) {
				LowerCaseString(extension);
			}
#endif
			return SString(fileNameWithLowerCaseExtension);
		} else {
			return props.Get(L"default.file.ext");
		}
	}
}

void SciTEBase::ForwardPropertyToEditor(const WCHAR *key) {
	SString value = props.Get(key);
	SendEditorString(SCI_SETPROPERTY,
	                 reinterpret_cast<uptr_t>(key), value.c_str());
	SendOutputString(SCI_SETPROPERTY,
	                 reinterpret_cast<uptr_t>(key), value.c_str());
}

void SciTEBase::DefineMarker(int marker, int markerType, ColourDesired fore, ColourDesired back) {
	SendEditor(SCI_MARKERDEFINE, marker, markerType);
	SendEditor(SCI_MARKERSETFORE, marker, fore.AsLong());
	SendEditor(SCI_MARKERSETBACK, marker, back.AsLong());
}

static int FileLength(const WCHAR *path) {
	int len = 0;
	FILE *fp = _wfopen(path, fileRead);
	if (fp) {
		fseek(fp, 0, SEEK_END);
		len = ftell(fp);
		fclose(fp);
	}
	return len;
}

void SciTEBase::ReadAPI(const SString &fileNameForExtension) {
	SString apisFileNames = props.GetNewExpand(L"api.",
	                        fileNameForExtension.c_str());
	size_t nameLength = apisFileNames.length();
	if (nameLength) {
		apisFileNames.substitute(L';', L'\0');
		const WCHAR *apiFileName = apisFileNames.c_str();
		const WCHAR *nameEnd = apiFileName + nameLength;

		int tlen = 0;    // total api length

		// Calculate total length
		while (apiFileName < nameEnd) {
			tlen += FileLength(apiFileName);
			apiFileName += wcslen(apiFileName) + 1;
		}

		// Load files
		if (tlen > 0) {
			WCHAR *buffer = apis.Allocate(tlen);
			if (buffer) {
				apiFileName = apisFileNames.c_str();
				tlen = 0;
				while (apiFileName < nameEnd) {
					FILE *fp = _wfopen(apiFileName, fileRead);
					if (fp) {
						fseek(fp, 0, SEEK_END);
						int len = ftell(fp);
						fseek(fp, 0, SEEK_SET);
						size_t readBytes = fread(buffer + tlen, 1, len, fp);
						tlen += readBytes;
						fclose(fp);
					}
					apiFileName += wcslen(apiFileName) + 1;
				}
				apis.SetFromAllocated();
			}
		}
	}
}

SString SciTEBase::FindLanguageProperty(const WCHAR *pattern, const WCHAR *defaultValue) {
	SString key = pattern;
	key.substitute(L"*", language.c_str());
	SString ret = props.GetExpanded(key.c_str());
	if (ret == L"")
		ret = props.GetExpanded(pattern);
	if (ret == L"")
		ret = defaultValue;
	return ret;
}

/**
 * A list of all the properties that should be forwarded to Scintilla lexers.
 */
static const WCHAR *propertiesToForward[] = {
//++Autogenerated -- run src/LexGen.py to regenerate
//**\(\t"\*",\n\)
	L"asp.default.language",
	L"fold",
	L"fold.at.else",
	L"fold.comment",
	L"fold.comment.nimrod",
	L"fold.comment.yaml",
	L"fold.compact",
	L"fold.directive",
	L"fold.html",
	L"fold.html.preprocessor",
	L"fold.hypertext.comment",
	L"fold.hypertext.heredoc",
	L"fold.perl.package",
	L"fold.perl.pod",
	L"fold.preprocessor",
	L"fold.quotes.nimrod",
	L"fold.quotes.python",
	L"fold.sql.exists",
	L"fold.sql.only.begin",
	L"fold.verilog.flags",
	L"html.tags.case.sensitive",
	L"lexer.caml.magic",
	L"lexer.cpp.allow.dollars",
	L"lexer.d.fold.at.else",
	L"lexer.errorlist.value.separate",
	L"lexer.html.mako",
	L"lexer.metapost.comment.process",
	L"lexer.metapost.interface.default",
	L"lexer.pascal.smart.highlighting",
	L"lexer.props.allow.initial.spaces",
	L"lexer.python.literals.binary",
	L"lexer.python.strings.b",
	L"lexer.python.strings.u",
	L"lexer.sql.backticks.identifier",
	L"lexer.tex.auto.if",
	L"lexer.tex.comment.process",
	L"lexer.tex.interface.default",
	L"lexer.tex.use.keywords",
	L"lexer.xml.allow.scripts",
	L"nsis.ignorecase",
	L"nsis.uservars",
	L"ps.level",
	L"ps.tokenize",
	L"sql.backslash.escapes",
	L"styling.within.preprocessor",
	L"tab.timmy.whinge.level",

//--Autogenerated -- end of automatically generated section

	0,
};

/* XPM */
static const WCHAR *bookmarkBluegem[] = {
/* width height num_colors chars_per_pixel */
L"    15    15      64            1",
/* colors */
L"  c none",
L". c #0c0630",
L"# c #8c8a8c",
L"a c #244a84",
L"b c #545254",
L"c c #cccecc",
L"d c #949594",
L"e c #346ab4",
L"f c #242644",
L"g c #3c3e3c",
L"h c #6ca6fc",
L"i c #143789",
L"j c #204990",
L"k c #5c8dec",
L"l c #707070",
L"m c #3c82dc",
L"n c #345db4",
L"o c #619df7",
L"p c #acacac",
L"q c #346ad4",
L"r c #1c3264",
L"s c #174091",
L"t c #5482df",
L"u c #4470c4",
L"v c #2450a0",
L"w c #14162c",
L"x c #5c94f6",
L"y c #b7b8b7",
L"z c #646464",
L"A c #3c68b8",
L"B c #7cb8fc",
L"C c #7c7a7c",
L"D c #3462b9",
L"E c #7c7eac",
L"F c #44464c",
L"G c #a4a4a4",
L"H c #24224c",
L"I c #282668",
L"J c #5c5a8c",
L"K c #7c8ebc",
L"L c #dcd7e4",
L"M c #141244",
L"N c #1c2e5c",
L"O c #24327c",
L"P c #4472cc",
L"Q c #6ca2fc",
L"R c #74b2fc",
L"S c #24367c",
L"T c #b4b2c4",
L"U c #403e58",
L"V c #4c7fd6",
L"W c #24428c",
L"X c #747284",
L"Y c #142e7c",
L"Z c #64a2fc",
L"0 c #3c72dc",
L"1 c #bcbebc",
L"2 c #6c6a6c",
L"3 c #848284",
L"4 c #2c5098",
L"5 c #1c1a1c",
L"6 c #243250",
L"7 c #7cbefc",
L"8 c #d4d2d4",
/* pixels */
L"    yCbgbCy    ",
L"   #zGGyGGz#   ",
L"  #zXTLLLTXz#  ",
L" p5UJEKKKEJU5p ",
L" lfISa444aSIfl ",
L" wIYij444jsYIw ",
L" .OsvnAAAnvsO. ",
L" MWvDuVVVPDvWM ",
L" HsDPVkxxtPDsH ",
L" UiAtxohZxtuiU ",
L" pNnkQRBRhkDNp ",
L" 1FrqoR7Bo0rF1 ",
L" 8GC6aemea6CG8 ",
L"  cG3l2z2l3Gc  ",
L"    1GdddG1    "
};

SString SciTEBase::GetFileNameProperty(const WCHAR *name) {
	SString namePlusDot = name;
	namePlusDot.append(L".");
	SString valueForFileName = props.GetNewExpand(namePlusDot.c_str(),
	        ExtensionFileName().c_str());
	if (valueForFileName.length() != 0) {
		return valueForFileName;
	} else {
		return props.Get(name);
	}
}

void SciTEBase::ReadProperties() {
	if (extender)
		extender->Clear();

	SString fileNameForExtension = ExtensionFileName();

	SString modulePath = props.GetNewExpand(L"lexerpath.",
	    fileNameForExtension.c_str());
	if (modulePath.length())
	    SendEditorString(SCI_LOADLEXERLIBRARY, 0, modulePath.c_str());
	language = props.GetNewExpand(L"lexer.", fileNameForExtension.c_str());
	if (language.length())
	    SendEditorString(SCI_SETLEXERLANGUAGE, 0, language.c_str());
	else
	    SendEditorString(SCI_SETLEXER, 0, SCLEX_CONTAINER);

	lexLanguage = SendEditor(SCI_GETLEXER);

	SendEditor(SCI_SETSTYLEBITS, SendEditor(SCI_GETSTYLEBITSNEEDED));

	SendOutput(SCI_SETLEXER, SCLEX_ERRORLIST);

	SString kw0 = props.GetNewExpand(L"keywords.", fileNameForExtension.c_str());
	SendEditorString(SCI_SETKEYWORDS, 0, kw0.c_str());

	for (int wl = 1; wl <= KEYWORDSET_MAX; wl++) {
		SString kwk(wl+1);
		kwk += L'.';
		kwk.insert(0, L"keywords");
		SString kw = props.GetNewExpand(kwk.c_str(), fileNameForExtension.c_str());
		SendEditorString(SCI_SETKEYWORDS, wl, kw.c_str());
	}

	FilePath homepath = GetSciteDefaultHome();
	props.Set(L"SciteDefaultHome", homepath.AsFileSystem());
	homepath = GetSciteUserHome();
	props.Set(L"SciteUserHome", homepath.AsFileSystem());

	for (size_t i=0; propertiesToForward[i]; i++) {
		ForwardPropertyToEditor(propertiesToForward[i]);
	}

	if (apisFileNames != props.GetNewExpand(L"api.",	fileNameForExtension.c_str())) {
		apis.Clear();
		ReadAPI(fileNameForExtension);
		apisFileNames = props.GetNewExpand(L"api.", fileNameForExtension.c_str());
	}

	props.Set(L"APIPath", apisFileNames.c_str());

	FilePath fileAbbrev = props.GetNewExpand(L"abbreviations.", fileNameForExtension.c_str()).c_str();
	if (!fileAbbrev.IsSet())
		fileAbbrev = GetAbbrevPropertiesFileName();
	if (!pathAbbreviations.SameNameAs(fileAbbrev)) {
		pathAbbreviations = fileAbbrev;
		ReadAbbrevPropFile();
	}

	DiscoverEOLSetting();
	props.Set(L"AbbrevPath", pathAbbreviations.AsFileSystem());

	codePage = props.GetInt(L"code.page");
	if (CurrentBuffer()->codepage != SC_CP_ACP) {
		// Override properties file to ensure Unicode displayed.
		codePage = CurrentBuffer()->codepage;
	}
	SendEditor(SCI_SETCODEPAGE, codePage);
	int outputCodePage = props.GetInt(L"output.code.page", codePage);
	SendOutput(SCI_SETCODEPAGE, outputCodePage);

	characterSet = props.GetInt(L"character.set", SC_CHARSET_DEFAULT);

	wrapStyle = props.GetInt(L"wrap.style", SC_WRAP_WORD);

	SendChildren(SCI_SETCARETFORE,
	           ColourOfProperty(props, L"caret.fore", ColourDesired(0, 0, 0)));

	SendChildren(SCI_SETMULTIPLESELECTION, props.GetInt(L"selection.multiple", 1));
	SendChildren(SCI_SETADDITIONALSELECTIONTYPING, props.GetInt(L"selection.additional.typing", 1));
	SendChildren(SCI_SETADDITIONALCARETSBLINK, props.GetInt(L"caret.additional.blinks", 1));
	SendChildren(SCI_SETVIRTUALSPACEOPTIONS, props.GetInt(L"virtual.space"));

	SendEditor(SCI_SETMOUSEDWELLTIME, props.GetInt(L"dwell.period", SC_TIME_FOREVER), 0);

	SendEditor(SCI_SETCARETWIDTH, props.GetInt(L"caret.width", 1));
	SendOutput(SCI_SETCARETWIDTH, props.GetInt(L"caret.width", 1));

	SString caretLineBack = props.Get(L"caret.line.back");
	if (caretLineBack.length()) {
		SendEditor(SCI_SETCARETLINEVISIBLE, 1);
		SendEditor(SCI_SETCARETLINEBACK, ColourFromString(caretLineBack));
	} else {
		SendEditor(SCI_SETCARETLINEVISIBLE, 0);
	}
	SendEditor(SCI_SETCARETLINEBACKALPHA,
		allowAlpha ? props.GetInt(L"caret.line.back.alpha", SC_ALPHA_NOALPHA) : SC_ALPHA_NOALPHA);

	SString findMark = props.Get(L"find.mark");
	if (findMark.length()) {
		SendEditor(SCI_INDICSETSTYLE, indicatorMatch, INDIC_ROUNDBOX);
		SendEditor(SCI_INDICSETFORE, indicatorMatch, ColourFromString(findMark));
	}

	SString controlCharSymbol = props.Get(L"control.char.symbol");
	if (controlCharSymbol.length()) {
		SendEditor(SCI_SETCONTROLCHARSYMBOL, static_cast<WCHAR>(controlCharSymbol[0]));
	} else {
		SendEditor(SCI_SETCONTROLCHARSYMBOL, 0);
	}

	SString caretPeriod = props.Get(L"caret.period");
	if (caretPeriod.length()) {
		SendEditor(SCI_SETCARETPERIOD, caretPeriod.value());
		SendOutput(SCI_SETCARETPERIOD, caretPeriod.value());
	}

	int caretSlop = props.GetInt(L"caret.policy.xslop", 1) ? CARET_SLOP : 0;
	int caretZone = props.GetInt(L"caret.policy.width", 50);
	int caretStrict = props.GetInt(L"caret.policy.xstrict") ? CARET_STRICT : 0;
	int caretEven = props.GetInt(L"caret.policy.xeven", 1) ? CARET_EVEN : 0;
	int caretJumps = props.GetInt(L"caret.policy.xjumps") ? CARET_JUMPS : 0;
	SendEditor(SCI_SETXCARETPOLICY, caretStrict | caretSlop | caretEven | caretJumps, caretZone);

	caretSlop = props.GetInt(L"caret.policy.yslop", 1) ? CARET_SLOP : 0;
	caretZone = props.GetInt(L"caret.policy.lines");
	caretStrict = props.GetInt(L"caret.policy.ystrict") ? CARET_STRICT : 0;
	caretEven = props.GetInt(L"caret.policy.yeven", 1) ? CARET_EVEN : 0;
	caretJumps = props.GetInt(L"caret.policy.yjumps") ? CARET_JUMPS : 0;
	SendEditor(SCI_SETYCARETPOLICY, caretStrict | caretSlop | caretEven | caretJumps, caretZone);

	int visibleStrict = props.GetInt(L"visible.policy.strict") ? VISIBLE_STRICT : 0;
	int visibleSlop = props.GetInt(L"visible.policy.slop", 1) ? VISIBLE_SLOP : 0;
	int visibleLines = props.GetInt(L"visible.policy.lines");
	SendEditor(SCI_SETVISIBLEPOLICY, visibleStrict | visibleSlop, visibleLines);

	SendEditor(SCI_SETEDGECOLUMN, props.GetInt(L"edge.column", 0));
	SendEditor(SCI_SETEDGEMODE, props.GetInt(L"edge.mode", EDGE_NONE));
	SendEditor(SCI_SETEDGECOLOUR,
	           ColourOfProperty(props, L"edge.colour", ColourDesired(0xff, 0xda, 0xda)));

	SString selFore = props.Get(L"selection.fore");
	if (selFore.length()) {
		SendChildren(SCI_SETSELFORE, 1, ColourFromString(selFore));
	} else {
		SendChildren(SCI_SETSELFORE, 0, 0);
	}
	SString selBack = props.Get(L"selection.back");
	if (selBack.length()) {
		SendChildren(SCI_SETSELBACK, 1, ColourFromString(selBack));
	} else {
		if (selFore.length())
			SendChildren(SCI_SETSELBACK, 0, 0);
		else	// Have to show selection somehow
			SendChildren(SCI_SETSELBACK, 1, ColourDesired(0xC0, 0xC0, 0xC0).AsLong());
	}
	int selectionAlpha = allowAlpha ? props.GetInt(L"selection.alpha", SC_ALPHA_NOALPHA) : SC_ALPHA_NOALPHA;
	SendChildren(SCI_SETSELALPHA, selectionAlpha);

	SString selAdditionalFore = props.Get(L"selection.additional.fore");
	if (selAdditionalFore.length()) {
		SendChildren(SCI_SETADDITIONALSELFORE, ColourFromString(selAdditionalFore));
	}
	SString selAdditionalBack = props.Get(L"selection.additional.back");
	if (selAdditionalBack.length()) {
		SendChildren(SCI_SETADDITIONALSELBACK, ColourFromString(selAdditionalBack));
	}
	int selectionAdditionalAlpha = (selectionAlpha == SC_ALPHA_NOALPHA) ? SC_ALPHA_NOALPHA : selectionAlpha / 2;
	SendChildren(SCI_SETADDITIONALSELALPHA, props.GetInt(L"selection.additional.alpha", selectionAdditionalAlpha));

	SString foldColour = props.Get(L"fold.margin.colour");
	if (foldColour.length()) {
		SendChildren(SCI_SETFOLDMARGINCOLOUR, 1, ColourFromString(foldColour));
	} else {
		SendChildren(SCI_SETFOLDMARGINCOLOUR, 0, 0);
	}
	SString foldHiliteColour = props.Get(L"fold.margin.highlight.colour");
	if (foldHiliteColour.length()) {
		SendChildren(SCI_SETFOLDMARGINHICOLOUR, 1, ColourFromString(foldHiliteColour));
	} else {
		SendChildren(SCI_SETFOLDMARGINHICOLOUR, 0, 0);
	}

	SString whitespaceFore = props.Get(L"whitespace.fore");
	if (whitespaceFore.length()) {
		SendChildren(SCI_SETWHITESPACEFORE, 1, ColourFromString(whitespaceFore));
	} else {
		SendChildren(SCI_SETWHITESPACEFORE, 0, 0);
	}
	SString whitespaceBack = props.Get(L"whitespace.back");
	if (whitespaceBack.length()) {
		SendChildren(SCI_SETWHITESPACEBACK, 1, ColourFromString(whitespaceBack));
	} else {
		SendChildren(SCI_SETWHITESPACEBACK, 0, 0);
	}

	WCHAR bracesStyleKey[200];
	wsprintf(bracesStyleKey, L"braces.%s.style", language.c_str());
	bracesStyle = props.GetInt(bracesStyleKey, 0);

	WCHAR key[200];
	SString sval;

	sval = FindLanguageProperty(L"calltip.*.ignorecase");
	callTipIgnoreCase = sval == L"1";

	calltipWordCharacters = FindLanguageProperty(L"calltip.*.word.characters", L"_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ");

	calltipParametersStart = FindLanguageProperty(L"calltip.*.parameters.start", L"(");
	calltipParametersEnd = FindLanguageProperty(L"calltip.*.parameters.end", L")");
	calltipParametersSeparators = FindLanguageProperty(L"calltip.*.parameters.separators", L",;");

	calltipEndDefinition = FindLanguageProperty(L"calltip.*.end.definition");

	wsprintf(key, L"autocomplete.%s.start.characters", language.c_str());
	autoCompleteStartCharacters = props.GetExpanded(key);
	if (autoCompleteStartCharacters == L"")
		autoCompleteStartCharacters = props.GetExpanded(L"autocomplete.*.start.characters");
	// "" is a quite reasonable value for this setting

	wsprintf(key, L"autocomplete.%s.fillups", language.c_str());
	autoCompleteFillUpCharacters = props.GetExpanded(key);
	if (autoCompleteFillUpCharacters == L"")
		autoCompleteFillUpCharacters =
			props.GetExpanded(L"autocomplete.*.fillups");
	SendEditorString(SCI_AUTOCSETFILLUPS, 0,
		autoCompleteFillUpCharacters.c_str());

	wsprintf(key, L"autocomplete.%s.ignorecase", L"*");
	sval = props.GetNewExpand(key);
	autoCompleteIgnoreCase = sval == L"1";
	wsprintf(key, L"autocomplete.%s.ignorecase", language.c_str());
	sval = props.GetNewExpand(key);
	if (sval != L"")
		autoCompleteIgnoreCase = sval == L"1";
	SendEditor(SCI_AUTOCSETIGNORECASE, autoCompleteIgnoreCase ? 1 : 0);
	SendOutput(SCI_AUTOCSETIGNORECASE, 1);

	int autoCChooseSingle = props.GetInt(L"autocomplete.choose.single");
	SendEditor(SCI_AUTOCSETCHOOSESINGLE, autoCChooseSingle),

	SendEditor(SCI_AUTOCSETCANCELATSTART, 0);
	SendEditor(SCI_AUTOCSETDROPRESTOFWORD, 0);

	if (firstPropertiesRead) {
		ReadPropertiesInitial();
	}

	ReadFontProperties();

	SendEditor(SCI_SETUSEPALETTE, props.GetInt(L"use.palette"));
	SendEditor(SCI_SETPRINTMAGNIFICATION, props.GetInt(L"print.magnification"));
	SendEditor(SCI_SETPRINTCOLOURMODE, props.GetInt(L"print.colour.mode"));

	jobQueue.clearBeforeExecute = props.GetInt(L"clear.before.execute");
	jobQueue.timeCommands = props.GetInt(L"time.commands");

	int blankMarginLeft = props.GetInt(L"blank.margin.left", 1);
	int blankMarginRight = props.GetInt(L"blank.margin.right", 1);
	SendEditor(SCI_SETMARGINLEFT, 0, blankMarginLeft);
	SendEditor(SCI_SETMARGINRIGHT, 0, blankMarginRight);
	SendOutput(SCI_SETMARGINLEFT, 0, blankMarginLeft);
	SendOutput(SCI_SETMARGINRIGHT, 0, blankMarginRight);

	SendEditor(SCI_SETMARGINWIDTHN, 1, margin ? marginWidth : 0);

	SString lineMarginProp = props.Get(L"line.margin.width");
	lineNumbersWidth = lineMarginProp.value();
	if (lineNumbersWidth == 0)
		lineNumbersWidth = lineNumbersWidthDefault;
	lineNumbersExpand = lineMarginProp.contains('+');

	SetLineNumberWidth();

	bufferedDraw = props.GetInt(L"buffered.draw", 1);
	SendEditor(SCI_SETBUFFEREDDRAW, bufferedDraw);

	twoPhaseDraw = props.GetInt(L"two.phase.draw", 1);
	SendEditor(SCI_SETTWOPHASEDRAW, twoPhaseDraw);

	SendEditor(SCI_SETLAYOUTCACHE, props.GetInt(L"cache.layout", SC_CACHE_CARET));
	SendOutput(SCI_SETLAYOUTCACHE, props.GetInt(L"output.cache.layout", SC_CACHE_CARET));

	bracesCheck = props.GetInt(L"braces.check");
	bracesSloppy = props.GetInt(L"braces.sloppy");

	SendEditor(SCI_SETCHARSDEFAULT);
	wordCharacters = props.GetNewExpand(L"word.characters.", fileNameForExtension.c_str());
	if (wordCharacters.length()) {
		SendEditorString(SCI_SETWORDCHARS, 0, wordCharacters.c_str());
	} else {
		wordCharacters = L"_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
	}

	whitespaceCharacters = props.GetNewExpand(L"whitespace.characters.", fileNameForExtension.c_str());
	if (whitespaceCharacters.length()) {
		SendEditorString(SCI_SETWHITESPACECHARS, 0, whitespaceCharacters.c_str());
	}

	SString viewIndentExamine = GetFileNameProperty(L"view.indentation.examine");
	indentExamine = viewIndentExamine.length() ? viewIndentExamine.value() : SC_IV_REAL;
	SendEditor(SCI_SETINDENTATIONGUIDES, props.GetInt(L"view.indentation.guides") ?
		indentExamine : SC_IV_NONE);

	SendEditor(SCI_SETTABINDENTS, props.GetInt(L"tab.indents", 1));
	SendEditor(SCI_SETBACKSPACEUNINDENTS, props.GetInt(L"backspace.unindents", 1));

	SendEditor(SCI_CALLTIPUSESTYLE, 32);

	indentOpening = props.GetInt(L"indent.opening");
	indentClosing = props.GetInt(L"indent.closing");
	indentMaintain = props.GetNewExpand(L"indent.maintain.", fileNameForExtension.c_str()).value();

	SString lookback = props.GetNewExpand(L"statement.lookback.", fileNameForExtension.c_str());
	statementLookback = lookback.value();
	statementIndent = GetStyleAndWords(L"statement.indent.");
	statementEnd = GetStyleAndWords(L"statement.end.");
	blockStart = GetStyleAndWords(L"block.start.");
	blockEnd = GetStyleAndWords(L"block.end.");

	SString list;
	list = props.GetNewExpand(L"preprocessor.symbol.", fileNameForExtension.c_str());
	preprocessorSymbol = list[0];
	list = props.GetNewExpand(L"preprocessor.start.", fileNameForExtension.c_str());
	preprocCondStart.Clear();
	preprocCondStart.Set(list.c_str());
	list = props.GetNewExpand(L"preprocessor.middle.", fileNameForExtension.c_str());
	preprocCondMiddle.Clear();
	preprocCondMiddle.Set(list.c_str());
	list = props.GetNewExpand(L"preprocessor.end.", fileNameForExtension.c_str());
	preprocCondEnd.Clear();
	preprocCondEnd.Set(list.c_str());

	memFiles.AppendList(props.GetNewExpand(L"find.files"));

	SendEditor(SCI_SETWRAPVISUALFLAGS, props.GetInt(L"wrap.visual.flags"));
	SendEditor(SCI_SETWRAPVISUALFLAGSLOCATION, props.GetInt(L"wrap.visual.flags.location"));
 	SendEditor(SCI_SETWRAPSTARTINDENT, props.GetInt(L"wrap.visual.startindent"));
 	SendEditor(SCI_SETWRAPINDENTMODE, props.GetInt(L"wrap.indent.mode"));

	if (props.GetInt(L"wrap.aware.home.end.keys",0)) {
		if (props.GetInt(L"vc.home.key", 1)) {
			AssignKey(SCK_HOME, 0, SCI_VCHOMEWRAP);
			AssignKey(SCK_HOME, SCMOD_SHIFT, SCI_VCHOMEWRAPEXTEND);
			AssignKey(SCK_HOME, SCMOD_SHIFT | SCMOD_ALT, SCI_VCHOMERECTEXTEND);
		} else {
			AssignKey(SCK_HOME, 0, SCI_HOMEWRAP);
			AssignKey(SCK_HOME, SCMOD_SHIFT, SCI_HOMEWRAPEXTEND);
			AssignKey(SCK_HOME, SCMOD_SHIFT | SCMOD_ALT, SCI_HOMERECTEXTEND);
		}
		AssignKey(SCK_END, 0, SCI_LINEENDWRAP);
		AssignKey(SCK_END, SCMOD_SHIFT, SCI_LINEENDWRAPEXTEND);
	} else {
		if (props.GetInt(L"vc.home.key", 1)) {
			AssignKey(SCK_HOME, 0, SCI_VCHOME);
			AssignKey(SCK_HOME, SCMOD_SHIFT, SCI_VCHOMEEXTEND);
			AssignKey(SCK_HOME, SCMOD_SHIFT | SCMOD_ALT, SCI_VCHOMERECTEXTEND);
		} else {
			AssignKey(SCK_HOME, 0, SCI_HOME);
			AssignKey(SCK_HOME, SCMOD_SHIFT, SCI_HOMEEXTEND);
			AssignKey(SCK_HOME, SCMOD_SHIFT | SCMOD_ALT, SCI_HOMERECTEXTEND);
		}
		AssignKey(SCK_END, 0, SCI_LINEEND);
		AssignKey(SCK_END, SCMOD_SHIFT, SCI_LINEENDEXTEND);
	}

	AssignKey('L', SCMOD_SHIFT | SCMOD_CTRL, SCI_LINEDELETE);

	scrollOutput = props.GetInt(L"output.scroll", 1);

	tabHideOne = props.GetInt(L"tabbar.hide.one");

	SetToolsMenu();

	SendEditor(SCI_SETFOLDFLAGS, props.GetInt(L"fold.flags"));

	// To put the folder markers in the line number region
	//SendEditor(SCI_SETMARGINMASKN, 0, SC_MASK_FOLDERS);

	SendEditor(SCI_SETMODEVENTMASK, SC_MOD_CHANGEFOLD);

	if (0==props.GetInt(L"undo.redo.lazy")) {
		// Trap for insert/delete notifications (also fired by undo
		// and redo) so that the buttons can be enabled if needed.
		SendEditor(SCI_SETMODEVENTMASK, SC_MOD_INSERTTEXT | SC_MOD_DELETETEXT
			| SC_LASTSTEPINUNDOREDO | SendEditor(SCI_GETMODEVENTMASK, 0));

		//SC_LASTSTEPINUNDOREDO is probably not needed in the mask; it
		//doesn't seem to fire as an event of its own; just modifies the
		//insert and delete events.
	}

	// Create a margin column for the folding symbols
	SendEditor(SCI_SETMARGINTYPEN, 2, SC_MARGIN_SYMBOL);

	SendEditor(SCI_SETMARGINWIDTHN, 2, foldMargin ? foldMarginWidth : 0);

	SendEditor(SCI_SETMARGINMASKN, 2, SC_MASK_FOLDERS);
	SendEditor(SCI_SETMARGINSENSITIVEN, 2, 1);

	switch (props.GetInt(L"fold.symbols")) {
	case 0:
		// Arrow pointing right for contracted folders, arrow pointing down for expanded
		DefineMarker(SC_MARKNUM_FOLDEROPEN, SC_MARK_ARROWDOWN,
		             ColourDesired(0, 0, 0), ColourDesired(0, 0, 0));
		DefineMarker(SC_MARKNUM_FOLDER, SC_MARK_ARROW,
		             ColourDesired(0, 0, 0), ColourDesired(0, 0, 0));
		DefineMarker(SC_MARKNUM_FOLDERSUB, SC_MARK_EMPTY,
		             ColourDesired(0, 0, 0), ColourDesired(0, 0, 0));
		DefineMarker(SC_MARKNUM_FOLDERTAIL, SC_MARK_EMPTY,
		             ColourDesired(0, 0, 0), ColourDesired(0, 0, 0));
		DefineMarker(SC_MARKNUM_FOLDEREND, SC_MARK_EMPTY,
		             ColourDesired(0xff, 0xff, 0xff), ColourDesired(0, 0, 0));
		DefineMarker(SC_MARKNUM_FOLDEROPENMID, SC_MARK_EMPTY,
		             ColourDesired(0xff, 0xff, 0xff), ColourDesired(0, 0, 0));
		DefineMarker(SC_MARKNUM_FOLDERMIDTAIL, SC_MARK_EMPTY, ColourDesired(0xff, 0xff, 0xff), ColourDesired(0, 0, 0));
		break;
	case 1:
		// Plus for contracted folders, minus for expanded
		DefineMarker(SC_MARKNUM_FOLDEROPEN, SC_MARK_MINUS, ColourDesired(0xff, 0xff, 0xff), ColourDesired(0, 0, 0));
		DefineMarker(SC_MARKNUM_FOLDER, SC_MARK_PLUS, ColourDesired(0xff, 0xff, 0xff), ColourDesired(0, 0, 0));
		DefineMarker(SC_MARKNUM_FOLDERSUB, SC_MARK_EMPTY, ColourDesired(0xff, 0xff, 0xff), ColourDesired(0, 0, 0));
		DefineMarker(SC_MARKNUM_FOLDERTAIL, SC_MARK_EMPTY, ColourDesired(0xff, 0xff, 0xff), ColourDesired(0, 0, 0));
		DefineMarker(SC_MARKNUM_FOLDEREND, SC_MARK_EMPTY, ColourDesired(0xff, 0xff, 0xff), ColourDesired(0, 0, 0));
		DefineMarker(SC_MARKNUM_FOLDEROPENMID, SC_MARK_EMPTY, ColourDesired(0xff, 0xff, 0xff), ColourDesired(0, 0, 0));
		DefineMarker(SC_MARKNUM_FOLDERMIDTAIL, SC_MARK_EMPTY, ColourDesired(0xff, 0xff, 0xff), ColourDesired(0, 0, 0));
		break;
	case 2:
		// Like a flattened tree control using circular headers and curved joins
		DefineMarker(SC_MARKNUM_FOLDEROPEN, SC_MARK_CIRCLEMINUS, ColourDesired(0xff, 0xff, 0xff), ColourDesired(0x40, 0x40, 0x40));
		DefineMarker(SC_MARKNUM_FOLDER, SC_MARK_CIRCLEPLUS, ColourDesired(0xff, 0xff, 0xff), ColourDesired(0x40, 0x40, 0x40));
		DefineMarker(SC_MARKNUM_FOLDERSUB, SC_MARK_VLINE, ColourDesired(0xff, 0xff, 0xff), ColourDesired(0x40, 0x40, 0x40));
		DefineMarker(SC_MARKNUM_FOLDERTAIL, SC_MARK_LCORNERCURVE, ColourDesired(0xff, 0xff, 0xff), ColourDesired(0x40, 0x40, 0x40));
		DefineMarker(SC_MARKNUM_FOLDEREND, SC_MARK_CIRCLEPLUSCONNECTED, ColourDesired(0xff, 0xff, 0xff), ColourDesired(0x40, 0x40, 0x40));
		DefineMarker(SC_MARKNUM_FOLDEROPENMID, SC_MARK_CIRCLEMINUSCONNECTED, ColourDesired(0xff, 0xff, 0xff), ColourDesired(0x40, 0x40, 0x40));
		DefineMarker(SC_MARKNUM_FOLDERMIDTAIL, SC_MARK_TCORNERCURVE, ColourDesired(0xff, 0xff, 0xff), ColourDesired(0x40, 0x40, 0x40));
		break;
	case 3:
		// Like a flattened tree control using square headers
		DefineMarker(SC_MARKNUM_FOLDEROPEN, SC_MARK_BOXMINUS, ColourDesired(0xff, 0xff, 0xff), ColourDesired(0x80, 0x80, 0x80));
		DefineMarker(SC_MARKNUM_FOLDER, SC_MARK_BOXPLUS, ColourDesired(0xff, 0xff, 0xff), ColourDesired(0x80, 0x80, 0x80));
		DefineMarker(SC_MARKNUM_FOLDERSUB, SC_MARK_VLINE, ColourDesired(0xff, 0xff, 0xff), ColourDesired(0x80, 0x80, 0x80));
		DefineMarker(SC_MARKNUM_FOLDERTAIL, SC_MARK_LCORNER, ColourDesired(0xff, 0xff, 0xff), ColourDesired(0x80, 0x80, 0x80));
		DefineMarker(SC_MARKNUM_FOLDEREND, SC_MARK_BOXPLUSCONNECTED, ColourDesired(0xff, 0xff, 0xff), ColourDesired(0x80, 0x80, 0x80));
		DefineMarker(SC_MARKNUM_FOLDEROPENMID, SC_MARK_BOXMINUSCONNECTED, ColourDesired(0xff, 0xff, 0xff), ColourDesired(0x80, 0x80, 0x80));
		DefineMarker(SC_MARKNUM_FOLDERMIDTAIL, SC_MARK_TCORNER, ColourDesired(0xff, 0xff, 0xff), ColourDesired(0x80, 0x80, 0x80));
		break;
	}

	SendEditor(SCI_MARKERSETFORE, markerBookmark,
	           ColourOfProperty(props, L"bookmark.fore", ColourDesired(0, 0, 0x7f)));
	SendEditor(SCI_MARKERSETBACK, markerBookmark,
	           ColourOfProperty(props, L"bookmark.back", ColourDesired(0x80, 0xff, 0xff)));
	SendEditor(SCI_MARKERSETALPHA,
		allowAlpha ? props.GetInt(L"bookmark.alpha", SC_ALPHA_NOALPHA) : SC_ALPHA_NOALPHA);
	SString bookMarkXPM = props.Get(L"bookmark.pixmap");
	if (bookMarkXPM.length()) {
		SendEditorString(SCI_MARKERDEFINEPIXMAP, markerBookmark,
			bookMarkXPM.c_str());
	} else if (props.Get(L"bookmark.fore").length()) {
		SendEditor(SCI_MARKERDEFINE, markerBookmark, SC_MARK_CIRCLE);
	} else {
		// No bookmark.fore setting so display default pixmap.
		SendEditorString(SCI_MARKERDEFINEPIXMAP, markerBookmark,
			reinterpret_cast<WCHAR *>(bookmarkBluegem));
	}

	SendEditor(SCI_SETSCROLLWIDTH, props.GetInt(L"horizontal.scroll.width", 2000));
	SendEditor(SCI_SETSCROLLWIDTHTRACKING, props.GetInt(L"horizontal.scroll.width.tracking", 1));
	SendOutput(SCI_SETSCROLLWIDTH, props.GetInt(L"output.horizontal.scroll.width", 2000));
	SendOutput(SCI_SETSCROLLWIDTHTRACKING, props.GetInt(L"output.horizontal.scroll.width.tracking", 1));

	// Do these last as they force a style refresh
	SendEditor(SCI_SETHSCROLLBAR, props.GetInt(L"horizontal.scrollbar", 1));
	SendOutput(SCI_SETHSCROLLBAR, props.GetInt(L"output.horizontal.scrollbar", 1));

	SendEditor(SCI_SETENDATLASTLINE, props.GetInt(L"end.at.last.line", 1));
	SendEditor(SCI_SETCARETSTICKY, props.GetInt(L"caret.sticky", 0));

	if (extender) {
		FilePath defaultDir = GetDefaultDirectory();
		FilePath scriptPath;

		// Check for an extension script
		SString extensionFile = props.GetNewExpand(L"extension.", fileNameForExtension.c_str());
		if (extensionFile.length()) {
			// find file in local directory
			FilePath docDir = filePath.Directory();
			if (Exists(docDir.AsInternal(), extensionFile.c_str(), &scriptPath)) {
				// Found file in document directory
				extender->Load(scriptPath.AsFileSystem());
			} else if (Exists(defaultDir.AsInternal(), extensionFile.c_str(), &scriptPath)) {
				// Found file in global directory
				extender->Load(scriptPath.AsFileSystem());
			} else if (Exists(L"", extensionFile.c_str(), &scriptPath)) {
				// Found as completely specified file name
				extender->Load(scriptPath.AsFileSystem());
			}
		}
	}
	firstPropertiesRead = false;
	needReadProperties = false;
}

void SciTEBase::ReadFontProperties() {
	WCHAR key[200];
	SString sval;

	// Set styles
	// For each window set the global default style, then the language default style, then the other global styles, then the other language styles

	SendEditor(SCI_STYLERESETDEFAULT, 0, 0);
	SendOutput(SCI_STYLERESETDEFAULT, 0, 0);

	wsprintf(key, L"style.%s.%0d", L"*", STYLE_DEFAULT);
	sval = props.GetNewExpand(key);
	SetOneStyle(wEditor, STYLE_DEFAULT, sval.c_str());
	SetOneStyle(wOutput, STYLE_DEFAULT, sval.c_str());

	wsprintf(key, L"style.%s.%0d", language.c_str(), STYLE_DEFAULT);
	sval = props.GetNewExpand(key);
	SetOneStyle(wEditor, STYLE_DEFAULT, sval.c_str());

	SendEditor(SCI_STYLECLEARALL, 0, 0);

	SetStyleFor(wEditor, L"*");
	SetStyleFor(wEditor, language.c_str());

	SendOutput(SCI_STYLECLEARALL, 0, 0);

	wsprintf(key, L"style.%s.%0d", L"errorlist", STYLE_DEFAULT);
	sval = props.GetNewExpand(key);
	SetOneStyle(wOutput, STYLE_DEFAULT, sval.c_str());

	SendOutput(SCI_STYLECLEARALL, 0, 0);

	SetStyleFor(wOutput, L"*");
	SetStyleFor(wOutput, L"errorlist");

	if (CurrentBuffer()->useMonoFont) {
		sval = props.GetExpanded(L"font.monospace");
		StyleDefinition sd(sval.c_str());
		for (int style = 0; style <= STYLE_MAX; style++) {
			if (style != STYLE_LINENUMBER) {
				if (sd.specified & StyleDefinition::sdFont) {
					SendEditorString(SCI_STYLESETFONT, style, sd.font.c_str());
				}
				if (sd.specified & StyleDefinition::sdSize) {
					SendEditor(SCI_STYLESETSIZE, style, sd.size);
				}
			}
		}
	}
}

// Properties that are interactively modifiable are only read from the properties file once.
void SciTEBase::SetPropertiesInitial() {
	splitVertical = props.GetInt(L"split.vertical");
	openFilesHere = props.GetInt(L"check.if.already.open");
	wrap = props.GetInt(L"wrap");
	wrapOutput = props.GetInt(L"output.wrap");
	indentationWSVisible = props.GetInt(L"view.indentation.whitespace", 1);
	sbVisible = props.GetInt(L"statusbar.visible");
	tbVisible = props.GetInt(L"toolbar.visible");
	tabVisible = props.GetInt(L"tabbar.visible");
	tabMultiLine = props.GetInt(L"tabbar.multiline");
	lineNumbers = props.GetInt(L"line.margin.visible");
	marginWidth = 0;
	SString margwidth = props.Get(L"margin.width");
	if (margwidth.length())
		marginWidth = margwidth.value();
	margin = marginWidth;
	if (marginWidth == 0)
		marginWidth = marginWidthDefault;
	foldMarginWidth = props.GetInt(L"fold.margin.width", foldMarginWidthDefault);
	foldMargin = foldMarginWidth;
	if (foldMarginWidth == 0)
		foldMarginWidth = foldMarginWidthDefault;

	matchCase = props.GetInt(L"find.replace.matchcase");
	regExp = props.GetInt(L"find.replace.regexp");
	unSlash = props.GetInt(L"find.replace.escapes");
	wrapFind = props.GetInt(L"find.replace.wrap", 1);
}

SString Localization::Text(const WCHAR *s, bool retainIfNotFound) {
	SString translation = s;
	int ellipseIndicator = translation.remove(L"...");
	int accessKeyPresent = translation.remove(menuAccessIndicator);
	translation.lowercase();
	translation.substitute(L"\n", L"\\n");
	translation = Get(translation.c_str());
	if (translation.length()) {
		if (ellipseIndicator)
			translation += L"...";
		if (0 == accessKeyPresent) {
#if PLAT_WIN
			// Following codes are required because accelerator is not always
			// part of alphabetical word in several language. In these cases,
			// accelerator is written like "(&O)".
			int posOpenParenAnd = translation.search(L"(&");
			if (posOpenParenAnd > 0 && translation.search(L")", posOpenParenAnd) == posOpenParenAnd+3) {
				translation.remove(posOpenParenAnd, 4);
			} else {
				translation.remove(L"&");
			}
#else
			translation.remove("&");
#endif
		}
		translation.substitute(L"&", menuAccessIndicator);
		translation.substitute(L"\\n", L"\n");
	} else {
		translation = missing;
	}
	if ((translation.length() > 0) || !retainIfNotFound) {
		return translation;
	}
	return s;
}

SString SciTEBase::LocaliseMessage(const WCHAR *s, const WCHAR *param0, const WCHAR *param1, const WCHAR *param2) {
	SString translation = localiser.Text(s);
	if (param0)
		translation.substitute(L"^0", param0);
	if (param1)
		translation.substitute(L"^1", param1);
	if (param2)
		translation.substitute(L"^2", param2);
	return translation;
}

void SciTEBase::ReadLocalization() {
	localiser.Clear();
	const WCHAR *title = L"locale.properties";
	SString localeProps = props.GetExpanded(title);
	if (localeProps.length()) {
		title = localeProps.c_str();
	}
	FilePath propdir = GetSciteDefaultHome();
	FilePath localePath(propdir, title);
	localiser.Read(localePath, propdir, importFiles, importMax);
	localiser.SetMissing(props.Get(L"translation.missing"));
	localiser.read = true;
}

void SciTEBase::ReadPropertiesInitial() {
	SetPropertiesInitial();
	int sizeHorizontal = props.GetInt(L"output.horizontal.size", 0);
	int sizeVertical = props.GetInt(L"output.vertical.size", 0);
	int hideOutput = props.GetInt(L"output.initial.hide", 0);
	if ((!splitVertical && (sizeVertical > 0) && (heightOutput < sizeVertical)) ||
		(splitVertical && (sizeHorizontal > 0) && (heightOutput < sizeHorizontal))) {
		previousHeightOutput = splitVertical ? sizeHorizontal : sizeVertical;
		if (!hideOutput) {
			heightOutput = NormaliseSplit(previousHeightOutput);
			SizeSubWindows();
			Redraw();
		}
	}
	ViewWhitespace(props.GetInt(L"view.whitespace"));
	SendEditor(SCI_SETINDENTATIONGUIDES, props.GetInt(L"view.indentation.guides") ?
		indentExamine : SC_IV_NONE);

	SendEditor(SCI_SETVIEWEOL, props.GetInt(L"view.eol"));
	SendEditor(SCI_SETZOOM, props.GetInt(L"magnification"));
	SendOutput(SCI_SETZOOM, props.GetInt(L"output.magnification"));
	SendEditor(SCI_SETWRAPMODE, wrap ? wrapStyle : SC_WRAP_NONE);
	SendOutput(SCI_SETWRAPMODE, wrapOutput ? wrapStyle : SC_WRAP_NONE);

	SString menuLanguageProp = props.GetNewExpand(L"menu.language");
	languageItems = 0;
	for (unsigned int i = 0; i < menuLanguageProp.length(); i++) {
		if (menuLanguageProp[i] == L'|')
			languageItems++;
	}
	languageItems /= 3;
	languageMenu = new LanguageMenuItem[languageItems];

	menuLanguageProp.substitute(L'|', L'\0');
	const WCHAR *sMenuLanguage = menuLanguageProp.c_str();
	for (int item = 0; item < languageItems; item++) {
		languageMenu[item].menuItem = sMenuLanguage;
		sMenuLanguage += wcslen(sMenuLanguage) + 1;
		languageMenu[item].extension = sMenuLanguage;
		sMenuLanguage += wcslen(sMenuLanguage) + 1;
		languageMenu[item].menuKey = sMenuLanguage;
		sMenuLanguage += wcslen(sMenuLanguage) + 1;
	}
	SetLanguageMenu();

	// load the user defined short cut props
	SString shortCutProp = props.GetNewExpand(L"user.shortcuts");
	if (shortCutProp.length()) {
		shortCutItems = 0;
		for (unsigned int i = 0; i < shortCutProp.length(); i++) {
			if (shortCutProp[i] == L'|')
				shortCutItems++;
		}
		shortCutItems /= 2;
		shortCutItemList = new ShortcutItem[shortCutItems];
		shortCutProp.substitute(L'|', L'\0');
		const WCHAR *sShortCutProp = shortCutProp.c_str();
		for (int item = 0; item < shortCutItems; item++) {
			shortCutItemList[item].menuKey = sShortCutProp;
			sShortCutProp += wcslen(sShortCutProp) + 1;
			shortCutItemList[item].menuCommand = sShortCutProp;
			sShortCutProp += wcslen(sShortCutProp) + 1;
		}
	}
	// end load the user defined short cut props


#if PLAT_WIN

	if (tabMultiLine) {	// Windows specific!
		long wl = ::GetWindowLong(reinterpret_cast<HWND>(wTabBar.GetID()), GWL_STYLE);
		::SetWindowLong(reinterpret_cast<HWND>(wTabBar.GetID()), GWL_STYLE, wl | TCS_MULTILINE);
	}
#endif

	FilePath homepath = GetSciteDefaultHome();
	props.Set(L"SciteDefaultHome", homepath.AsFileSystem());
	homepath = GetSciteUserHome();
	props.Set(L"SciteUserHome", homepath.AsFileSystem());
}

FilePath SciTEBase::GetDefaultPropertiesFileName() {
	return FilePath(GetSciteDefaultHome(), propGlobalFileName);
}

FilePath SciTEBase::GetAbbrevPropertiesFileName() {
	return FilePath(GetSciteUserHome(), propAbbrevFileName);
}

FilePath SciTEBase::GetUserPropertiesFileName() {
	return FilePath(GetSciteUserHome(), propUserFileName);
}

FilePath SciTEBase::GetLocalPropertiesFileName() {
	return FilePath(filePath.Directory(), propLocalFileName);
}

FilePath SciTEBase::GetDirectoryPropertiesFileName() {
	FilePath propfile;

	if (filePath.IsSet()) {
		propfile.Set(filePath.Directory(), propDirectoryFileName);

		// if this file does not exist try to find the prop file in a parent directory
		while (!propfile.Directory().IsRoot() && !propfile.Exists()) {
			propfile.Set(propfile.Directory().Directory(), propDirectoryFileName);
		}

		// not found -> set it to the initial directory
		if (!propfile.Exists()) {
			propfile.Set(filePath.Directory(), propDirectoryFileName);
		}
	}
	return propfile;
}

void SciTEBase::OpenProperties(int propsFile) {
	FilePath propfile;
	switch (propsFile) {
	case IDM_OPENLOCALPROPERTIES:
		propfile = GetLocalPropertiesFileName();
		Open(propfile, ofQuiet);
		break;
	case IDM_OPENUSERPROPERTIES:
		propfile = GetUserPropertiesFileName();
		Open(propfile, ofQuiet);
		break;
	case IDM_OPENABBREVPROPERTIES:
		propfile = pathAbbreviations;
		Open(propfile, ofQuiet);
		break;
	case IDM_OPENGLOBALPROPERTIES:
		propfile = GetDefaultPropertiesFileName();
		Open(propfile, ofQuiet);
		break;
	case IDM_OPENLUAEXTERNALFILE: {
			SString extlua = props.GetExpanded(L"ext.lua.startup.script");
			if (extlua.length()) {
				Open(extlua.c_str(), ofQuiet);
			}
			break;
		}
	case IDM_OPENDIRECTORYPROPERTIES: {
			propfile = GetDirectoryPropertiesFileName();
			bool alreadyExists = propfile.Exists();
			Open(propfile, ofQuiet);
			if (!alreadyExists)
				SaveAsDialog();
		}
		break;
	}
}

// return the int value of the command name passed in.
int SciTEBase::GetMenuCommandAsInt(SString commandName) {
	int i = IFaceTable::FindConstant(commandName.c_str());
	if (i != -1) {
		return IFaceTable::constants[i].value;
	}
	// Otherwise we might have entered a number as command to access a "SCI_" command
	return commandName.value();
}
